import qs from 'qs'
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus'
import { LoginParam } from '@/api/user/userModel'
import { cleanSession, getToken } from "@/utils/auth"


// 返回值类型
// Result默认是泛型类型，默认any类型。（如果传了就是T类型,如果没有传就是默认的any类型）
export interface Result<T = any> {
    code: number,
    msg: string,
    data: T
}

// 返回的状态码 ts枚举
export enum StatusCode {
    NoAuth = 600, // 无权限
    Success = 200 // 返回成功
}

class request {
    // 实例
    private instance:AxiosInstance 

    // 构造函数 给instance进行初始化
    constructor(config:AxiosRequestConfig) {
        // 创建axios实例
        this.instance = axios.create(config)
        // 拦截器配置
        this.interceptors()
    }

    private interceptors() {
        // 请求发送之前的拦截器：添加token
        this.instance.interceptors.request.use((config:AxiosRequestConfig) => {
            // console.log('axios请求的config===', config)
            // 配置token
            let token = getToken()
            // console.log(token)
            // 如果token存在,就放在请求的头部
            if(token) {
                config.headers = { 
                    ...config.headers,
                    token: token 
                }
            }
            return config
        },(error: any) => {
            console.log(error)
            error.data = {}
            error.data.msg = '服务器异常,请联系管理员!'
            return error
        })

        /**
         * 请求返回之后拦截
         * res的类型是AxiosResponse<any>
         */
        this.instance.interceptors.response.use((res: AxiosResponse) => {
            // console.log(res)
            if(res && res.data) {
                const data = res.data as any
                // 后端接口会返回一些状态,code是根据后端定义的字段来写的
                if(data.code == StatusCode.NoAuth) {
                    console.log('token过期,跳转到登录===')
                    window.location.href = '/login'
                    cleanSession()
                } else if(data.code ==StatusCode.Success || res.config.responseType === "arraybuffer") {
                    // 数据返回成功
                    // console.log(456)
                    return res
                } else {
                    ElMessage.error(data.msg || '服务器出错')
                    return res || null
                }
            }
        },(error) => {
            console.log(error)
            if (error && error.response) { 
                switch (error.response.status) { 
                    case 400: error.data.msg = '错误请求'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 401: error.data.msg = '未授权，请重新登录'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 403: error.data.msg = '拒绝访问'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 404: error.data.msg = '请求错误,未找到该资源'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 405: error.data.msg = '请求方法未允许'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 408: error.data.msg = '请求超时'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 500: error.data.msg = '服务器端出错'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 501: error.data.msg = '网络未实现'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 502: error.data.msg = '网络错误'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 503: error.data.msg = '服务不可用'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 504: error.data.msg = '网络超时'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    case 505: error.data.msg = 'http版本不支持该请求'; 
                        ElMessage.error(error.data.msg) 
                        break 
                    default: error.data.msg = `连接错误${error.response.status}`; 
                        ElMessage.error(error.data.msg) 
                } 
            } else { 
                console.log(error)
                // error.data.msg = "连接到服务器失败";
                error.data = "连接到服务器失败";
                ElMessage.error(error.data.msg) 
            }
            return error
        })
    }

    // { userId: 10 } 参数序列化
    // http:localhost:8080/api/getUserByid?userId=10
    get<T = any>(url: string, param?:any): Promise<Result<T>> {
        return new Promise((resolve,reject) => {
            this.instance.get<T>(url,{
                params: param,
                paramsSerializer:(param) => {
                    return qs.stringify(param)
                }
            }).then((res)=> {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    // 参数的处理
    getParam(param:any) {
        let _param = ''
        if(Object.is(param,undefined || null)) {
            _param = ''
        } else {
            for(const key in param) {
                // 判断对象中是否有指定的属性
                if(param.hasOwnProperty(key) && param[key]) {
                    _param += `${param[key]}/`
                }
            }
        }
        if(_param) {
            _param = _param.substr(0,_param.length -1)
            return _param
        }
    }

    // { userId: 10,userName: 'afanfan' }  ==> 10/test
    // restful API 形式 http:localhost:8080/api/getUserByid/10/test
    // url: http:localhost:8080/api/getUserByid
    getRestApi<T= any>(url:string,param?:any):Promise<Result<T>> {
        return new Promise((resolve,reject) => {
            this.instance.get<T>(this.getParam(param) ? `${url}/${this.getParam(param)}` : url).then(res => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    // post请求
    // `transformRequest` 允许在向服务器发送前，修改请求数据.且只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
    post<T= any>(url:string,param: any):Promise<Result<T>> {
        return new Promise((resolve,reject) => {
            this.instance.post<T>(url,param, {
                transformRequest: [(params) => {
                    return JSON.stringify(params)
                }],
                headers: {
                    'Content-Type': 'application/json'
                }
            }).then(res => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    // put请求
    put<T= any>(url:string,param: any):Promise<Result<T>> {
        return new Promise((resolve,reject) => {
            this.instance.put<T>(url,param, {
                transformRequest: [(params) => {
                    return JSON.stringify(params)
                }],
                headers: {
                    'Content-Type': 'application/json'
                }
            }).then(res => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    // 删除
    delete<T= any>(url:string,param?:any):Promise<Result<T>> {
        return new Promise((resolve,reject) => {
            this.instance.delete<T>(this.getParam(param) ? `${url}/${this.getParam(param)}` : url).then(res => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    // 获取验证码
    getImage(url:string) {
        return this.instance.post(url,null,{
            responseType: "arraybuffer"
        })
    }

    // 登录
    login<T>(url:string,param:LoginParam): Promise<Result<T>> {
        return new Promise((resolve,reject) => {
            console.log(param)
            this.instance.post<T>(url,param,{
                transformRequest: [(param) => {
                    // console.log(qs.stringify(param)) // username=admin&password=1234&code=fsfd
                    return qs.stringify(param)
                }], 
                // 接口文档上对Content-Type有做要求，所以这里要写成application/x-www-form-urlencoded
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            }).then(res => {
                resolve(res as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }
}

export default request